package com.kz.framework.proxy;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.kz.framework.annotation.aop.MyAfter;
import com.kz.framework.annotation.aop.MyBefore;
import com.kz.framework.helper.BeanHelper;

/**
 * @author zhgp
 * @version 1.0
 * @description: TODO
 * @date 2021/7/2 22:54
 */
public class JdkDynamicAopProxy implements InvocationHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(JdkDynamicAopProxy.class);

    /**
     * 解决多线程调用代理链的错乱问题 代理链调用索引设置为线程私有变量初始值为 0 如果是成员变量会有线程安全问题，多个线程之间的代理链会错乱
     */
    private ThreadLocal<Integer> localIndex = ThreadLocal.withInitial(() -> 0);

    /*
     *目标对象
     */
    private Object target;

    public JdkDynamicAopProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        int chainsIndex = localIndex.get();
        Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
        Annotation[] targetAnnotations = targetMethod.getDeclaredAnnotations();

        /**
         * 没有注解不代理
         */
        if (targetAnnotations.length <= 0) {
            return invoke(method, args);
        }
        List<ProxyChain> proxyChains = new ArrayList<>(8);

        /**
         * 获取这个方法的所有的注解，获取所有配置注解的切面方法
         */
        Arrays.stream(targetAnnotations).forEach(annotation -> {
            Class<? extends Annotation> annotationClass = annotation.annotationType();
            if (BeanHelper.PROXY_RULE.containsKey(annotationClass)) {
                proxyChains.addAll(BeanHelper.PROXY_RULE.get(annotationClass));
            }
        });

        /**
         * 拦截为空则直接执行目标方法
         */
        if (CollectionUtils.isEmpty(proxyChains)) {
            return invoke(method, args);
        }

        /**
         * 调用链完了执行目标方法
         */
        if (chainsIndex == proxyChains.size()) {
            return invoke(method, args);
        }

        /**
         * 构建传入下一个拦截器的参数
         */
        ProceedingJoinPoint proceedingJoinPoint = new ProceedingJoinPoint();
        proceedingJoinPoint.setArgs(args);
        proceedingJoinPoint.setMethod(method);
        proceedingJoinPoint.setTarget(proxy);

        ProxyChain proxyChain = proxyChains.get(chainsIndex);

        /**
         * 对前置，后置操作的特殊处理
         */
        Method proxyMethod = proxyChain.getMethod();
        if (proxyMethod.getAnnotation(MyBefore.class) != null || proxyMethod.getAnnotation(MyAfter.class) != null) {
            proxyChain.setPoint(proceedingJoinPoint);
        }

        proxyChain.setArgs(new Object[] {proceedingJoinPoint});

        /**
         * 索引+1，执行下一个
         */
        localIndex.set(chainsIndex + 1);

        /**
         * 执行下一个拦截器
         */
        return proxyChain.invoker();
    }

    private Object invoke(Method method, Object[] args) throws Throwable {
        /**
         * 执行目标方法时，索引初始化， 避免线程池线程复用导致的线程安全问题
         */
        localIndex.set(0);
        return method.invoke(target, args);
    }
}
